﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using YawetagLib.Security;
using YawetagLib.Services;

namespace YawetagLib.Facade
{
    public abstract class BaseFacade : IFacade
    {
        private static readonly object _lock = new object();

        static BaseFacade()
        {
            GenericMethodInfo = new Dictionary<Type, MethodInfo>();
        }

        protected BaseFacade(IFacadeFactory facadeFactory, IServiceFactory serviceBroker)
        {
            FacadeFactory = facadeFactory;
            ServiceBroker = serviceBroker;

            InitializeAtHelperMethodInfo();
        }

        private void InitializeAtHelperMethodInfo()
        {
            ServiceBrokerGetInfo = GetMethodGetInfo(ServiceBroker.GetType());
            FacadeFactoryGetInfo = GetMethodGetInfo(FacadeFactory.GetType());
        }

        private static MethodInfo GetMethodGetInfo(IReflect classType)
        {
            return classType
                .GetMethods(BindingFlags.Instance | BindingFlags.Public)
                .Where(i => i.IsGenericMethod && i.Name == "Get")
                .Single();
        }

        private static MethodInfo GetGenericMethodInfo(MethodInfo methodInfo, Type type)
        {
            lock (_lock)
            {
                if (!GenericMethodInfo.ContainsKey(type))
                {
                    GenericMethodInfo.Add(type, methodInfo.MakeGenericMethod(type));
                }
            }

            return GenericMethodInfo[type];
        }

        protected T At<T>() where T : class
        {
            Type type = typeof(T);

            if (typeof(IService).IsAssignableFrom(type))
            {
                return (T)GetGenericMethodInfo(ServiceBrokerGetInfo, type).Invoke(ServiceBroker, null);
            }
            if (typeof(IFacade).IsAssignableFrom(type))
            {
                return (T)GetGenericMethodInfo(FacadeFactoryGetInfo, type).Invoke(FacadeFactory, null);
            }

            throw new Exception("At Helper's generic parameter must implement either IService or IFacade interface");
        }

        protected UserContext UserContext
        {
            get { return Thread.CurrentPrincipal as UserContext; }
        }

        protected IServiceFactory ServiceBroker { get; private set; }
        protected IFacadeFactory FacadeFactory { get; private set; }

        private MethodInfo ServiceBrokerGetInfo { get; set; }
        private MethodInfo FacadeFactoryGetInfo { get; set; }
        private static IDictionary<Type, MethodInfo> GenericMethodInfo { get; set; }
    }
}
